package j2EE.tag;

import j2EE.bean.RSSInfo;
import j2EE.bean.impl.RSSInfoImpl;
import lombok.extern.log4j.Log4j;

import java.util.*;

/**
 * 允许将订阅添加到不同的RSS URL
 *
 * @author Guojt
 * @date 2024/11/29 19:31
 */

@Log4j
public class RSSSubscriber extends Thread {

    private static final int UPDATE_FREQ = 1000 * 30;

    //订阅的内部标识
    class RSSSubscription implements Comparable {
        private String url;
        // 下一次时间
        private long nextUpdate;
        // 上一次时间
        private long updateFreq;


        // 更具下一次更新时间排序
        @Override
        public int compareTo(Object obj) {
            RSSSubscription rssSubscription = (RSSSubscription) obj;

            if (rssSubscription.nextUpdate > this.updateFreq) {
                return -1;
            } else if (rssSubscription.nextUpdate < this.updateFreq) {
                return 1;
            } else {
                // 如果时间相同，则比较url （递归）
                return url.compareTo(rssSubscription.url);
            }
        }
    }

    // 按下一次更新时间排序的一组订阅
    private SortedSet subscriptions;
    private Map cache;
    private boolean quit = false;

    // singleton 订阅者
    private static RSSSubscriber subscriber;

    // 获取一个singleton 的引用
    public static RSSSubscriber getInstance() {
        if (subscriber == null) {
            subscriber = new RSSSubscriber();
            subscriber.start();
        }
        return subscriber;
    }

    RSSSubscriber() {
        subscriptions = new TreeSet();

        // 创建一个线程安全的map 确保多线程访问map 不会出错
        // 但是 在检查一个键是否存在后立即插入 还是需要外部保证线程安全
        cache = Collections.synchronizedMap(new HashMap<>());

        // 设置为守护线程 并且在线程启动之前设置
        // 确保当主线程退出时，子线程也会退出
        setDaemon(true);
    }

    // 从缓存中取得一个RSSInfo 对象，如果不在则创建一个新的订阅
    public RSSInfo getInfo(String url) throws Exception {
        if (cache.containsKey(url)) {
            return (RSSInfo) cache.get(url);
        }

        RSSInfo rssInfo = new RSSInfoImpl();
        rssInfo.parse(url);
        cache.put(url, rssInfo);

        // 创建一个订阅
        RSSSubscription newSub = new RSSSubscription();
        newSub.url = url;
        newSub.nextUpdate = UPDATE_FREQ;
        putSubscription(newSub);
        return rssInfo;
    }

    // 增加一个订阅
    private synchronized void putSubscription(RSSSubscription newSub) {
        newSub.nextUpdate = System.currentTimeMillis() + newSub.updateFreq;
        subscriptions.add(newSub);

        notify();
    }

    // 等待需要更新的下一个订阅
    private synchronized RSSSubscription getSubscription() {
        while (!quit) {
            while (subscriptions.isEmpty()) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    log.error("线程等待异常" + e);
                }
            }

            // 取得队列中首个订阅者
            RSSSubscription nextSub = (RSSSubscription) subscriptions.first();
            // 判断是否可以更新
            long now = System.currentTimeMillis();
            if (now >= nextSub.nextUpdate) {
                subscriptions.remove(nextSub);
                return nextSub;
            }

            // 下次更新前休眠，如果增加一个订阅者则中断
            try {
                wait(nextSub.nextUpdate - now);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }
        return null;
    }


    // 当订阅者可以得到时进行更新
    public void run() {
        while (!quit) {
            RSSSubscription subscription = getSubscription();
            try {
                RSSInfo rssInfo = new RSSInfoImpl();
                rssInfo.parse(subscription.url);
                cache.put(subscription.url, rssInfo);

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            putSubscription(subscription);
        }
    }

    public synchronized void quit() {
        quit = true;
        notify();
    }
}
